﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using smp.QueryDb.Structure;

namespace smp.QueryDb
{
    internal class Helper
    {
        internal static PropertyInfo GetExpressionProperty(Expression propertyExpression)
        {
            if (propertyExpression == null)
                return null;
            if (propertyExpression.NodeType == ExpressionType.Convert)
            {
                propertyExpression = ((UnaryExpression)propertyExpression).Operand;
            }
            if (propertyExpression.NodeType != ExpressionType.MemberAccess)
                return null;
            var memberExpression = (MemberExpression)propertyExpression;
            if (memberExpression.Expression == null || memberExpression.Expression.NodeType != ExpressionType.Parameter)
                return null;
            var property = (PropertyInfo)memberExpression.Member;
            if (property.CanRead)
                return property;
            return null;
        }

        internal static PropertyInfo GetNamedProperty<T>(string propertyName)
        {
            return typeof (T).GetProperty(propertyName);
        }

        internal static Exception Error(string format, params object[] parameters)
        {
            InstanceFactory.Log.Error(format, parameters);
            return new Exception(string.Format(format, parameters));
        }

        internal static Exception LogException(Exception ex)
        {
            InstanceFactory.Log.Error(ex);
            return ex;
        }

        internal static bool IsCollection(PropertyInfo property)
        {
            return GetCollectionType(property) != null;
        }

        internal static Type GetCollectionType(PropertyInfo property)
        {
            Type propertyType = property.PropertyType;
            if (property.PropertyType.IsGenericType &&
                   property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                return new NullableConverter(propertyType).UnderlyingType;
            }
            Type[] typeArray = propertyType.GetGenericArguments();
            if (typeArray.Length == 0)
                return null;
            return typeArray[typeArray.Length - 1];
        }

        internal static Column GetMapColumn(PropertyInfo property, List<Map> maps)
        {
            foreach(var map in maps)
            {
                if(map.Column == null)
                    continue;
                if(map.Property.Name == property.Name)
                    return map.Column;
            }
            return null;
        }

        internal static IDbCommand CreateAdoCommand(IDbConnection connection, ICommand command, CommandType commandType, IDbTransaction transaction)
        {
            var adoCommand = connection.CreateCommand();
            adoCommand.CommandType = commandType;
            adoCommand.CommandText = command.CommandText;
            if(transaction != null)
            {
                adoCommand.Transaction = transaction;
            }
            foreach (var parameter in command.Parameters)
            {
                var adoParameter = adoCommand.CreateParameter();
                adoParameter.DbType = parameter.DataType;
                adoParameter.Direction = parameter.Direction;
                if(parameter.Value == null)
                {
                    adoParameter.Value = DBNull.Value;
                }
                else
                {
                    adoParameter.Value = parameter.Value;
                }
                adoParameter.ParameterName = parameter.Name;
                adoCommand.Parameters.Add(adoParameter);
            }

            if(InstanceFactory.ShowSql)
            {
                LogExecution(adoCommand);
            }

            return adoCommand;
        }

        private static void LogExecution(IDbCommand command)
        {
            var text = new StringBuilder();
            text.AppendFormat("Executing : {0}", command.CommandText);
            if (command.Parameters != null && command.Parameters.Count > 0)
            {
                text.Append(" Parameters : ");
                foreach (IDataParameter parameter in command.Parameters)
                {
                    text.AppendFormat(" {0}='{1}',", parameter.ParameterName, parameter.Value);
                }
            }
            InstanceFactory.Log.Debug(text.ToString());
        }

        internal static object ConvertType(object value, Type targetType)
        {
            if (value == null)
            {
                return null;
            }
            if (ReferenceEquals(value, ""))
            {
                return "";
            }
            if (value.ToString().Length == 0)
            {
                return null;
            }

            if(targetType == typeof(byte[]))
            {
                return value;
            }

            if (targetType.IsGenericType && targetType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
            {
                targetType = new NullableConverter(targetType).UnderlyingType;
            }
            if (targetType.IsEnum)
            {
                return Enum.Parse(targetType, value.ToString(), true);
            }
            if (targetType == typeof(Guid))
            {
                return new Guid(value.ToString());
            }
            return Convert.ChangeType(value.ToString().Trim(), Type.GetTypeCode(targetType));
        }
    }
}
